package cn.com.dcsgo.config;

import cn.com.dcsgo.constant.BusinessEnum;
import cn.com.dcsgo.constant.HttpConstant;
import cn.com.dcsgo.constant.ResourceConstant;
import cn.com.dcsgo.filter.TokenTranslateFilter;
import cn.com.dcsgo.model.Result;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;

/**
 * 配置哪些接口需要认证、如何处理认证/授权失败等
 *
 * @author Dcsgo
 * @since 2025/9/20 10:57
 */
@Slf4j
@Configuration
@EnableMethodSecurity
public class ResourceServerConfig {
    @Resource
    public TokenTranslateFilter tokenTranslateFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();// 禁用 CSRF
        http.cors().disable();// 禁用跨域
        // 无状态会话（不创建、使用 HTTP 会话
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 在登录认证前先进行 Token 解析（先走登录认证的话，那每个请求都要登录一遍了）
        http.addFilterBefore(tokenTranslateFilter, UsernamePasswordAuthenticationFilter.class);
        // 配置异常处理
        http.exceptionHandling()
                // 认证失败处理器（如：无 Token 访问受保护接口）
                .authenticationEntryPoint(authenticationEntryPoint())
                // 授权失败处理器（如：有 Token 但无权限）
                .accessDeniedHandler(accessDeniedHandler());
        // 配置接口授权规则
        http.authorizeHttpRequests()
                // 白名单接口（无需认证直接访问）
                .requestMatchers(ResourceConstant.RESOURCE_ALLOW_URLS).permitAll()
                // 其他所有接口必须认证后才能访问
                .anyRequest().authenticated();
        return http.build();
    }

    /**
     * 处理授权失败（如 Token 有效但用户无权限访问接口）
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return (request, response, accessDeniedException) -> {
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            String path = request.getRequestURI();
            String ip = request.getRemoteAddr();
            log.error("权限不足，访问路径为:{},IP为:{}", path, ip);
            Result<Object> result = Result.fail(BusinessEnum.ACCESS_DENY_FAIL);
            String jsonResult = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(jsonResult);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 处理认证失败（如请求无 Token、Token 无效）
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> {
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            String path = request.getRequestURI();
            String ip = request.getRemoteAddr();
            log.error("非法访问，访问路径为:{},IP为:{}", path, ip);
            Result<Object> result = Result.fail(BusinessEnum.UN_AUTHORIZATION);
            String jsonResult = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(jsonResult);
            writer.flush();
            writer.close();
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
